/**
 * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights
 * reserved. <a>http://www.cqcis.com</a>
 */
package com.cqcis.uip.base.framework.codec.text;

import com.cqcis.uip.base.framework.UIPServiceException;
import com.cqcis.uip.base.framework.codec.ObjectDecoder;
import com.cqcis.uip.base.framework.common.HttpParam;
import com.cqcis.uip.base.framework.common.HttpParam.ParamContent;
import com.cqcis.uip.base.framework.utils.ByteBuffer;
import com.cqcis.uip.base.framework.utils.StringUtil;

/**
 * HTTP 解码
 * 
 * @author huangmy
 * @date   2008-12-4
 */
public class HttpProtocolDecoder implements ObjectDecoder {
	private static int MAX_LENGTH = 1024 * 1024 * 512; // 512M
	private int maxlength = MAX_LENGTH;
	
	public HttpProtocolDecoder() {		
	}
	
	public HttpProtocolDecoder(int maxlength) {	
		this.maxlength = maxlength;
	}

	// 解析
	public Object decode(ByteBuffer buffer) throws Exception {
		HttpParam param = new HttpParam();
		
		param.setSession(buffer.getSession());
				
		int limit = buffer.limit();
		int contentLength = 0;
		boolean isGet = false;		
		ByteBuffer sub = ByteBuffer.allocate(256).setAutoExpand(true);
		boolean encodingChunked = false;
		boolean isFirstline = false;
		
		while (buffer.hasRemaining() || (limit - buffer.position()) > 0) {
			byte b = buffer.get();
			sub.put(b);
			if (b != 0x0A) {
				continue;
			}
			
			sub.flip();
			String t = new String(sub.array(), HttpParam.HEADER_ENCODE);
			sub.clear();
			t = StringUtil.trimWhiteToNull(t);
			if (t == null) { // 空白行
				int remain = limit - buffer.position();
				if (remain >= contentLength && contentLength > 0) { // xml 文件内容	
					ByteBuffer xmlBuf = ByteBuffer.allocate(1024).setAutoExpand(true);
					xmlBuf.put(buffer);
					xmlBuf.flip();
					
					param.setContents(xmlBuf);
					
					return param;
				}
				
				if (isGet) {
					return param;
				}
				
				if (encodingChunked) {// read chunked style
					ByteBuffer xmlBuf = readChunkedBuf(buffer);
					if (xmlBuf != null) {
						param.setContents(xmlBuf);							
						return param;
					}
				}
				
				break;
			}
			
			if (!isFirstline) {
				param.setHeaderline(t);
				if (t.indexOf(HttpParam.GET) >= 0) {
					isGet = true;
				}
				isFirstline = true;
			} else {
				String[] ts = t.split(HttpParam.PARAM_SPLIT);
				if (ts == null || ts.length != 2) {
					throw new UIPServiceException("Invalid Header: " + t);
				}
				ParamContent content = new ParamContent(ts[0], ts[1]);

				param.addParams(content);

				String name = content.getName().toLowerCase();
				if (name.indexOf(HttpParam.CONTENT_LENGTH.toLowerCase()) >= 0) {// 长度
					String length = content.getValue();
					contentLength = Integer.parseInt(StringUtil
							.trimWhiteToNull(length));
					if (contentLength > this.maxlength) {
						throw new UIPServiceException("the content-length is too long, the length can't be more than " 
								+ this.maxlength + " .");
					}
				}

				String value = content.getValue() == null ? "" : content
						.getValue().toLowerCase();
				if (name.indexOf(HttpParam.TRANSFER_ENCODING.toLowerCase()) >= 0
						&& value.indexOf(HttpParam.CHUNKED.toLowerCase()) >= 0) {
					encodingChunked = true;
				}
			}
		}
				
		return null;
	}
	
	/**
	 * 只读取一个chunked的数据
	 * 
	 * @param from
	 * @return
	 */
	private ByteBuffer readChunkedBuf(ByteBuffer from) {
		ByteBuffer tmpBuf = ByteBuffer.allocate(64).setAutoExpand(true);
		ByteBuffer buf = null;
		
		while (from.hasRemaining()) {
			byte b = from.get();
			tmpBuf.put(b);
			if (b != 0x0A) {
				continue;
			}
			
			tmpBuf.flip();
			
			String length = new String(tmpBuf.array());
			length = StringUtil.trimWhiteToNull(length);
			int len = Integer.parseInt(length, 16);
			if (len > this.maxlength) {
				throw new UIPServiceException("the chunked-length is too long, the length can't be more than " 
						+ this.maxlength + " .");
			}
			
			tmpBuf.clear();
			
			if (from.remaining() < (len + 2)) {
				return null;
			}
			
			int oldlimit = from.limit();
			from.limit(from.position() + len);
			ByteBuffer bufFor = ByteBuffer.allocate(1024).setAutoExpand(true);
			bufFor.put(from);
			bufFor.flip();
			from.limit(oldlimit);
			
			if (buf == null) {
				buf = bufFor;
			}
			
			from.get(); // 0x0D
			from.get(); // 0x0A
		}
		
		return buf;
	}
}
